package com.eta.user.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.eta.common.constant.ResultCodeEnum;
import com.eta.common.exception.BusinessException;
import com.eta.common.utils.JwtUtils;
import com.eta.common.utils.RedisCache;
import com.eta.user.mapper.UserAuthMapper;
import com.eta.user.model.dto.UserLoginDto;
import com.eta.user.model.dto.UserRegisterDto;
import com.eta.user.model.po.SysUser;
import com.eta.user.model.vo.UserLoginVO;
import com.eta.user.service.UserAuthService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author 星河一叶Ck
 * @date 2025/9/3
 * @description 用户权限服务实现类
 */

@Service
@Slf4j
@RequiredArgsConstructor
public class UserAuthServiceImpl implements UserAuthService {
    private final UserAuthMapper userAuthMapper;
    private final RedisCache redisCache;
    private final JwtUtils jwtUtils;

    // 密码最大错误次数
    @Value("${login.password.max-errors: 5}")
    private int maxPasswordErrors;

    // 账户锁定时间（分钟）
    @Value("${login.lock-duration: 5}")
    private int lockDuration;

    /**
     * 注册
     * @param userRegisterDto 注册信息
     */
    @Override
    public void register(UserRegisterDto userRegisterDto) {
        //TODO 短信功能待开发
        validateRegisterParam(userRegisterDto);

        // 加密
        String encryptedPwd = BCrypt.hashpw(userRegisterDto.getPassword(), BCrypt.gensalt());

        Long nextUserId = userAuthMapper.getNextUserId();
        if (nextUserId == null) {
            throw new BusinessException(ResultCodeEnum.USER_REGISTER_ERROR);
        }

        // 生成用户名
        String formattedNum = String.format("%07d", nextUserId);
        String userName = "Eta_" + formattedNum;

        SysUser sysUser = new SysUser();
        BeanUtil.copyProperties(userRegisterDto, sysUser);
        sysUser.setPassword(encryptedPwd);
        sysUser.setUserName(userName);
        sysUser.setStatus(1);
        sysUser.setUserType(0);
        sysUser.setIsDeleted(0);
        sysUser.setVersion(0);

        // 原子插入
        int insertResult = userAuthMapper.insertWithUniqueCheck(sysUser);
        if (insertResult == 0) {
            throw new BusinessException(ResultCodeEnum.USER_ACCOUNT_EXIST);
        }
    }

    /**
     * 登录
     * @param userLoginDto 用户登录信息
     */
    @Override
    public UserLoginVO login(UserLoginDto userLoginDto) {

        if (userLoginDto.getLoginType() == 0) {
            // 账号密码登录
            return passwordLogin(userLoginDto);
        } else if (userLoginDto.getLoginType() == 1) {
            //TODO 验证码校验功能或短信功能待开发
            throw new BusinessException(ResultCodeEnum.LOGIN_TYPE_NOT_SUPPORT);
        } else {
            throw new BusinessException(ResultCodeEnum.LOGIN_TYPE_NOT_SUPPORT);
        }
    }

    /**
     * 密码登录
     * @param userLoginDto 用户登录信息
     * @return 登录信息
     */
    private UserLoginVO passwordLogin(UserLoginDto userLoginDto) {
        String account = userLoginDto.getAccount();
        String password = userLoginDto.getPassword();
        String loginIp = userLoginDto.getLoginIp();

        // 检查用户是否被锁定
        checkAccountLocked(account);

        // 查询用户信息
        SysUser sysUser = userAuthMapper.selectOne(new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getAccount, account));

        if (sysUser == null) {
            handlePasswordError(account);
            throw new BusinessException(ResultCodeEnum.USER_NOT_EXIST);
        }

        // 账户状态校验
        if(sysUser.getStatus() != 1) {
            throw new BusinessException(ResultCodeEnum.USER_ACCOUNT_DISABLED);
        }

        // 密码校验
        if (!BCrypt.checkpw(password, sysUser.getPassword())) {
            handlePasswordError(account);
            throw new BusinessException(ResultCodeEnum.USER_PASSWORD_ERROR);
        }

        // 登陆成功：重置登录次数 + 更新登录信息
        resetPasswordErrorCount(account);

        // 生成令牌
        String accessToken = jwtUtils.generateToken(sysUser.getId().toString());
        String refreshToken = jwtUtils.generateRefreshToken(sysUser.getId().toString());
        cacheUserToken(sysUser.getId(), accessToken);

        return buildLoginVO(sysUser, accessToken, refreshToken);
    }

    /**
     * 检查账号是否被锁定
     * @param account 账号
     */
    private void checkAccountLocked(String account) {
        String lockKey = "login:lock:" + account;
        Object isLocked = redisCache.getCacheObject(lockKey);
        if (Boolean.TRUE.equals(isLocked) || "1".equals(isLocked)) {
            throw new BusinessException(ResultCodeEnum.USER_ACCOUNT_LOCKED);
        }
    }


    // 处理错误密码 （累计次数并锁定）
    private void handlePasswordError(String account) {
        String errorKey = "login:password:error" + account;
        Long errorCount = redisCache.increment(errorKey, 1, 24, TimeUnit.HOURS);

        if (errorCount != null && errorCount >= maxPasswordErrors) {
            String lockKey = "login:lock:" + account;
            redisCache.setCacheObject(lockKey, "1", lockDuration, TimeUnit.MINUTES);
            log.warn("账户被锁定: {}, 错误次数： {}", account, errorCount);
        }
    }

    // 重置密码错误次数
    private void resetPasswordErrorCount(String account) {
        String errorKey = "login:password:error" + account;
        redisCache.deleteObject(errorKey);
    }

    // 缓存用户令牌
    private void cacheUserToken(Long userId, String token) {
        String tokenKey = "login:token:" + userId;
        redisCache.setCacheObject(tokenKey, tokenKey, jwtUtils.getExpireTime(), TimeUnit.HOURS);
    }

    // 组装登录响应VO
    private UserLoginVO buildLoginVO (SysUser sysUser, String accessToken, String refreshToken) {
        UserLoginVO userLoginVO = new UserLoginVO();
        BeanUtil.copyProperties(sysUser, userLoginVO);
        userLoginVO.setToken(accessToken);
        userLoginVO.setRefreshToken(refreshToken);
        userLoginVO.setExpireTime(LocalDateTime.now().plusMinutes(jwtUtils.getExpireTime()));
        return userLoginVO;
    }

    // 校验注册基本参数
    private void validateRegisterParam(UserRegisterDto registerDto) {
        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
        boolean hasPhone = StringUtils.hasText(registerDto.getPhone());
        boolean hasEmail = StringUtils.hasText(registerDto.getEmail());
        if (hasPhone && hasEmail) {
            queryWrapper.eq(SysUser::getPhone, registerDto.getPhone())
                    .or()
                    .eq(SysUser::getEmail, registerDto.getEmail());
        } else if (hasPhone) {
            queryWrapper.eq(SysUser::getPhone, registerDto.getPhone());
        } else if (hasEmail) {
            queryWrapper.eq(SysUser::getEmail, registerDto.getEmail());
        }

        SysUser sysUser = userAuthMapper.selectOne(queryWrapper);

        if (sysUser != null) {
            if (hasPhone && Objects.equals(sysUser.getPhone(), registerDto.getPhone())) {
                throw new BusinessException(ResultCodeEnum.USER_PHONE_EXIST);
            }
            if (hasEmail && Objects.equals(sysUser.getEmail(), registerDto.getEmail())) {
                throw new BusinessException(ResultCodeEnum.USER_EMAIL_EXIST);
            }
        }
    }
}